﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Text;

namespace Collector.Device
{
    class PulseThreeBodyPartsSegmentCollector : PulseGeneralCollector
    {

        private readonly string TAG = "PulseThreeBodyPartsSegmentCollector:";
        private readonly int MAXMIN_QUEUE_LENGTH = 1000;
        private double mThresholdRadio = 0.0;
        // 记录目标压力值列表，进行分段加压 Unit: mmHg
        private List<int> mListTargetPressure = new List<int>();
        private ReaderWriterLockSlim mRWLock = new ReaderWriterLockSlim();
        private AutoResetEvent conditionWait = new AutoResetEvent(false);
        private IDeviceCommunication mDeviceCommunication;
        private SubSectionState mSubSectionState = SubSectionState.eSTATE_IDLE;
        // 存储当前分段稳定采集的有效压力值
        Queue<double> mPulsePressureQueue = new Queue<double>();
        private bool mUserRightHandSel = true;
        private string mPhoneNumber = "137xxxxxxxx";
        private string mSerialNumber = "snxxxxxxx";
        // 脉象数据保存的目录
        private string mRawDataPath;
        // 脉象原始数据保留文件
        private string[] mRawDataFile;
        private int mSegmentIndex = 0;
        bool isShuttingDown = false;

        public CollectorUINotifyHandler<PulseCommonHelper.Display> notifyMainForm;
        //public event CollectorStateObserver mObserver;
        // 0 - idle, 1 - started 2 - collected 3 -  collect finished  4 - follow finished
        private Action<CollectorState, int> mObserver;

        // 脉象sensor寸关尺分别对应的静态压力
        private Dictionary<BodyPart, double> mThreeBodyPartBaseLineMap = new Dictionary<BodyPart, double>();

        // 脉象实时压力采样点，只用于实时显示脉压值
        private Dictionary<BodyPart, Queue<double>> mThreePluseDataMap = new Dictionary<BodyPart, Queue<double>>
        {
            {BodyPart.eBODY_PART_CUN, new Queue<double>()},
            {BodyPart.eBODY_PART_GUAN, new Queue<double>()},
            {BodyPart.eBODY_PART_CHI, new Queue<double>()}
        };

        private readonly Dictionary<int, BodyPart> mThreeBodyPartIndexMap = new Dictionary<int, BodyPart>
        {
            {0, BodyPart.eBODY_PART_CUN},
            {1, BodyPart.eBODY_PART_GUAN},
            {2, BodyPart.eBODY_PART_CHI}
        };

        private enum SubSectionState
        {
            eSTATE_RISING,
            // 已加压到目标值
            eSTATE_REACHED,
            // 已加压到目标值进行稳定采样中
            eSTATE_STABLE_COLLECTING,
            // 目标值已经采集完成
            eSTATE_FINISHED,
            // 设备空闲, 此时设备应该是复位状态
            eSTATE_IDLE
        }

        /// <summary>
        /// @param baseline 基线压力 mBar
        /// @param targetPressureList 目标压力值 mmHg
        /// </summary>
        /// <param name="baseline"></param>
        public PulseThreeBodyPartsSegmentCollector(Dictionary<BodyPart, double> baseLineMap, List<int> targetPressureList)
        {
            foreach (var key in baseLineMap.Keys)
            {
                mThreeBodyPartBaseLineMap.Add(key, baseLineMap[key]);
            }
            targetPressureList.ForEach(item => mListTargetPressure.Add(item));
        }

        public double ThresholdRadio
        {
            get { return mThresholdRadio; }
            set { mThresholdRadio = value; }
        }

        public IDeviceCommunication DeviceCommunication
        {
            get { return mDeviceCommunication; }
            set { DeviceAdapter = mDeviceCommunication = value; }
        }

        public bool UserRightHandSel
        {
            get { return mUserRightHandSel; }
            set { mUserRightHandSel = value; }
        }

        public string PhoneNumber
        {
            get { return mPhoneNumber; }
            set { mPhoneNumber = value; }
        }

        public string SerialNumber
        {
            get { return mSerialNumber; }
            set { mSerialNumber = value; }
        }

        public string RawDataPath
        {
            get { return mRawDataPath; }
            set { mRawDataPath = value; }
        }

        public string[] RawDataFile
        {
            get { return mRawDataFile; }
        }

        public void AddObserver(Action<CollectorState, int> observer)
        {
            mObserver += observer;
        }

        public bool ResetDevice()
        {
            if(isShuttingDown)
            {
                Log.Debug(TAG + "Device already reset completed.");
                return true;
            }
            return DeviceAdapter.ResetDevice();
        }

        public bool DoSubsectionProcessing()
        {
            bool ret = false;

            // 检查加压段数，至少有一段
            if (mListTargetPressure.Count == 0)
            {
                Log.Error(TAG + "DoSubsectionProcessing() target pressure list is null");
                return false;
            }

            mRawDataFile = new string[mListTargetPressure.Count];

            // 确保条件是无信号状态
            conditionWait.Reset();

            // 执行加压段数目标阈值
            for (int i = 0; i < mListTargetPressure.Count; i++)
            {
                byte[] payload;
                // Convert mmHg to Pa
                int targetPressure = (int)(mListTargetPressure[i] * 133.33);
                DisplayMessageToUI(string.Format("分段加压 {0}mmHg,Waiting for {1}S...", mListTargetPressure[i], (MAXMIN_QUEUE_LENGTH / 100)), LogMsgType.DEBUG);

                // Reset pulse pressure queue.
                mPulsePressureQueue.Clear();
                foreach (var kvt in mThreePluseDataMap)
                {
                    kvt.Value.Clear();
                }

                // 创建采集数据的存储文件
                mRawDataFile[i] = CreateSaveRawDataFile();
                mSegmentIndex = i;

                // started
                NotifyObserver(CollectorState.eCOLLECTOR_STATE_START_TRIGGER, 0);

                // 发送分段加压指令
                ret = setTargetPrussure(targetPressure);
                if (!ret)
                    return ret;

                // Reset pulse subsection state
                Log.Info(TAG + "DoSubsectionProcessing() change state to eSTATE_RISING");
                setSubsectionState(SubSectionState.eSTATE_RISING);

                // 等待脉压数据收集完成
                ret = waitTargetPressureReached();
                if (!ret)
                    break;

                if (pulseDataProcessing(mListTargetPressure[i]) == false)
                    break;
            }

            // 流程结束
            NotifyObserver(CollectorState.eCOLLECTOR_STATE_COMPLETED, 0);

            // 等待100ms 确保最后的脉图固定
            conditionWait.Reset();
            conditionWait.WaitOne(200);
            return ret;
        }

        private bool setTargetPrussure(int pressure)
        {
            byte[] payload;
            // 获取加压指令头部
            byte[] cmdHeader = FileConfig.getSerialPortData("SPHeadmmHgData");
            // +2 是分配目标压力值2字节空间
            byte[] final_byteCmd = new byte[cmdHeader.Length + 2];
            cmdHeader.CopyTo(final_byteCmd, 0);
            // padding target High byte
            final_byteCmd[final_byteCmd.Length - 2] = (byte)((pressure >> 8) & 0xFF);
            // Padding target Low byte
            final_byteCmd[final_byteCmd.Length - 1] = (byte)(pressure & 0xFF);
            //Log.Debug(TAG + "setTargetPrussure:SendCmd_mmHg :" + string.Format("{0}", string.Join(" ", final_byteCmd.Select(p => string.Format("{0:X2}", p)))));
            bool ret = mDeviceCommunication.SendCommandToDevice(final_byteCmd, "SPHeadmmHgData", 0x92, out payload);
            if (ret && payload[0] != 0x00)
            {
                ret = false;
                Log.Error(TAG + "setTargetPrussure() request the device failed to increase pressure");
            }

            return ret;
        }

        private bool waitTargetPressureReached()
        {
            // 默认已经检测到加压到位
            bool ret = true;
            SubSectionState state = getSubsectionState();
            try
            {
                // 检查是否加压已经到位，否则等待
                if (state != SubSectionState.eSTATE_REACHED)
                {
                    Log.Info(TAG + "waitTargetPressureReached() Wait STATE_REACHED");
                    // 等待加压到位，等待10s
                    ret = conditionWait.WaitOne(25 * 1000);
                }
            }
            finally
            {
                // 等待稳定脉压数据收集
                if (ret && !isShuttingDown)
                {
                    Log.Info(TAG + "waitTargetPressureReached() change state from STATE_REACHED to STATE_STABLE_COLLECTING");
                    DisplayMessageToUI("目标加压完成，等待10s数据采集中...", LogMsgType.INFO);
                    setSubsectionState(SubSectionState.eSTATE_STABLE_COLLECTING);
                    ret = conditionWait.WaitOne(15 * 1000);
                    if (ret && !isShuttingDown)
                    {                        
                        Log.Info(TAG + "waitTargetPressureReached() change state from STATE_STABLE_COLLECTING to STATE_FINISHED");
                        DisplayMessageToUI("数据收集完成", LogMsgType.INFO);
                    } 
                    else
                    {
                        Log.Error(TAG + "waitTargetPressureReached() wait state of STATE_FINISHED is failed");
                    }
                }
            }

            if (isShuttingDown)
            {
                ret = false;
                Log.Error(TAG + "waitTargetPressureReached() wait state is shutdown");
            }

            return ret;
        }

        /**
         * 处理收集的脉压数据。
         * 检查脉压数据压力范围，均值、最大均值、最小均值、最大最小均值差
         * 
         * @param targetValue 当前分段目标压力值 mmHg
         */
        private bool pulseDataProcessing(int targetValue /*mmHg*/)
        {
            // Unit: Pa
            double pulse_raw_data_average = 0.0;
            // Unit: mmHg 减去基线压力值
            double pulse_data_without_baseline_average = 0.0;
            double takeData_Last = 0.0;
            double takeData_First = 0.0;
            double diffpPValue = 0.0;

            DisplayMessageToUI("MaxMin_WaveData.Count :" + mThreePluseDataMap[BodyPart.eBODY_PART_CUN].Count, LogMsgType.INFO);
            if (mThreePluseDataMap[BodyPart.eBODY_PART_CUN].Count != MAXMIN_QUEUE_LENGTH)
            {
                DisplayMessageToUI(string.Format("脉压数据采集有误 {0}", mThreePluseDataMap[BodyPart.eBODY_PART_CUN].Count), LogMsgType.ERROR);
                return false;
            }

            // 计算有效脉压数据的平均值
            foreach (var item in mThreeBodyPartIndexMap)
            {
                pulse_raw_data_average = Math.Round(mThreePluseDataMap[item.Value].Average(), 2);
                pulse_data_without_baseline_average = Math.Round(((pulse_raw_data_average - mThreeBodyPartBaseLineMap[item.Value])) / 133.33, 2);
                DisplayMessageToUI(string.Format("MinValue:{0}mmHg, CurValue:{1}mmHg",
                        (targetValue * (1 - mThresholdRadio)), pulse_data_without_baseline_average), LogMsgType.INFO);

                // 1000脉压数据,取前10位平均值
                takeData_First = Math.Round((mThreePluseDataMap[item.Value].Take(10).Average() - mThreeBodyPartBaseLineMap[item.Value]) / 133, 2);
                // 1000脉压数据,取最后10位平均值
                takeData_Last = Math.Round((mThreePluseDataMap[item.Value].Reverse().Take(10).Average() - mThreeBodyPartBaseLineMap[item.Value]) / 133, 2);
                diffpPValue = Math.Round((takeData_First - takeData_Last), 2);

                Log.Debug(TAG + string.Format("averagepPValue_mmHg:{0}->{1}, diff_mmHg:{2}->{3}, FrontValue :{4}, LastValue :{5}",
                        pulse_data_without_baseline_average, targetValue, diffpPValue, mThresholdRadio,
                        takeData_First, takeData_Last), LogMsgType.INFO);

            }

            return true;
        }

        private void setSubsectionState(SubSectionState state, bool needRelease = false)
        {
            mRWLock.TryEnterWriteLock(200);
            try
            {
                if (mSubSectionState != state)
                {
                    mSubSectionState = state;
                    // 通知状态更新
                    if (needRelease)
                        conditionWait.Set();
                }
            }
            finally
            {
                mRWLock.ExitWriteLock();
            }
        }

        private SubSectionState getSubsectionState()
        {
            SubSectionState state = SubSectionState.eSTATE_IDLE;
            mRWLock.TryEnterReadLock(200);
            state = mSubSectionState;
            mRWLock.ExitReadLock();

            return state;
        }

        public static bool ByteToFile(byte[] byteArray, string fileName)
        {
            bool result = false;
            byte[] padding = { 0x00, 0x00, 0x00, 0x00, 0x00 };
            try
            {
                using (FileStream fs = new FileStream(fileName, FileMode.Append, FileAccess.Write))
                {
                    fs.Write(byteArray, 0, byteArray.Length);
                    //fs.Write(padding, 0, 5);
                    result = true;
                }
            }
            catch
            {
                result = false;
            }
            return result;
        }

        public override void ControlFrameProcessing(byte[] frame)
        {

            DeviceEvent evt = (DeviceEvent)frame[IDeviceProtocolShape.FRAME_FIELD_CMD_OFFSET];
            Log.Debug(TAG + "ControlFrameProcessing() {0} evt code: {1}", ConvertFuncs.ByteArrayToHexString(frame), evt);
            switch (evt)
            {
                case DeviceEvent.eDEV_EVENT_REACHED:
                    {
                        DeviceSegmentRespondCode code = (DeviceSegmentRespondCode)frame[IDeviceProtocolShape.FRAME_FIELD_PAYLOAD_OFFSET];
                        switch (code)
                        {
                            case DeviceSegmentRespondCode.eDEV_SEGMENT_ACK_CODE_COMPLETED:
                                {
                                    // 回复设备收到A7事件
                                    if (mDeviceCommunication != null)
                                    {
                                        mDeviceCommunication.GivebackCompleted(evt);
                                    }

                                    // 更新状态
                                    if (getSubsectionState() != SubSectionState.eSTATE_STABLE_COLLECTING)
                                    {
                                        setSubsectionState(SubSectionState.eSTATE_REACHED, true);
                                        // collect
                                        NotifyObserver(CollectorState.eCOLLECTOR_STATE_CAPTURING, 0);
                                    }
                                }
                                break;
                            case DeviceSegmentRespondCode.eDEV_SEGMENT_ACK_CODE_RISING_TIMEOUT:
                                {
                                    // Todo: 可能需要互斥判断，避免信号多发
                                    if (getSubsectionState() != SubSectionState.eSTATE_STABLE_COLLECTING)
                                    {
                                        setSubsectionState(SubSectionState.eSTATE_FINISHED, true);
                                        NotifyObserver(CollectorState.eCOLLECTOR_STATE_CAPTURE_FINISHED, 0);
                                    }
                                }
                                break;
                            case DeviceSegmentRespondCode.eDEV_SEGMENT_ACK_CODE_MEASUING_TIMEOUT:
                                {
                                    // nothing
                                }
                                break;
                            case DeviceSegmentRespondCode.eDEV_SEGMENT_ACK_CODE_HOST_RESET:
                                {
                                    isShuttingDown = true;
                                    if(getSubsectionState() != SubSectionState.eSTATE_IDLE)
                                        setSubsectionState(SubSectionState.eSTATE_FINISHED, true);
                                    NotifyObserver(CollectorState.eCOLLECTOR_STATE_COMPLETED, 0);
                                }
                                break;
                        }
                    }
                    break;
                case DeviceEvent.eDEV_EVENT_OVERFLOW:
                    {
                        
                    }
                    break;
                case DeviceEvent.eDEV_EVENT_RESET:
                    {
                        // 回复设备收到A4事件
                        if (mDeviceCommunication != null)
                        {
                            mDeviceCommunication.GivebackCompleted(evt);
                        }

                        // Log.Debug(TAG + "ControlFrameProcessing() device already reset completed");
                        // 更新状态
                        setSubsectionState(SubSectionState.eSTATE_IDLE);
                    }
                    break;
                default:
                    {
                        base.ControlFrameProcessing(frame);
                    }
                    break;
            }
        }

        /**
         * 解析一帧有效的脉压帧数据
         * 脉压帧格式:
         * ---------------------------------------------------------------------------------------------------------------------------------------------------------------
         * |              |                    |                                                       Payload(12Byte)                                                   |
         * | Header(4Byte)| PayloadSize(1Byte) |--------------------------------------------------------------------------------------------------------------------------
         * |              |                    |           snr0(3Byte)        |           snr1(3Byte)        |           snr2(3Byte)        | SnrType |     Seq(2Byte)   |
         * ---------------------------------------------------------------------------------------------------------------------------------------------------------------
         * | BB 66 BB 66  |       0x0C         | H(1Byte)| M(1Byte)| L(1Byte) | H(1Byte)| M(1Byte)| L(1Byte) | H(1Byte)| M(1Byte)| L(1Byte) |   0x04  | L(1Byte)|H(1Byte)|
         * ---------------------------------------------------------------------------------------------------------------------------------------------------------------
         * 
         * @param frame 一帧有效的脉压帧数据
         * 
         */
        public override void PulseFrameProcessing(byte[] frame)
        {
            //Log.Debug("==>frame: " + ConvertFuncs.ByteArrayToHexString(frame) + ", size:{0} ", frame.Length);
            SubSectionState state = getSubsectionState();
            if (state != SubSectionState.eSTATE_STABLE_COLLECTING)
            {
                return;
            }

            byte payload_size = frame[IDeviceProtocolShape.FRAME_FIELD_LENGTH_OFFSET];
            int end_pos = 4 + (int)payload_size;
            byte sensorType = frame[end_pos - 2];

            ///
            /// TODO: Should to check frame size to make sure frame is availd.
            ///
            foreach (var item in mThreeBodyPartIndexMap)
            {
                int i = item.Key;
                int d = (frame[IDeviceProtocolShape.FRAME_FIELD_PULSE_DATA_L + i * 3]) |
                        (frame[IDeviceProtocolShape.FRAME_FIELD_PULSE_DATA_M + i * 3] << 8) |
                        (frame[IDeviceProtocolShape.FRAME_FIELD_PULSE_DATA_H + i * 3] << 16);
                double ratio = sensorType == 3 ? 32 : 1;
                // Unit: Pa
                double pPData = d / ratio;

                var part = item.Value;
                mThreePluseDataMap[part].Enqueue(pPData);
                if (mThreePluseDataMap[part].Count >= MAXMIN_QUEUE_LENGTH)
                {
                    // 收集有效的脉压数据已经完成
                    setSubsectionState(SubSectionState.eSTATE_FINISHED, true);
                    NotifyObserver(CollectorState.eCOLLECTOR_STATE_CAPTURE_FINISHED, 0);
                }
            }

            // 保存有效数据
            if (mRawDataFile[mSegmentIndex] != null)
                ByteToFile(frame, mRawDataFile[mSegmentIndex]);
        }

        public override void AirFrameProcessing(byte[] frame) { }

        private void NotifyObserver(CollectorState state, int product)
        {
            mObserver?.Invoke(state, product);
        }

        /// <summary>
        /// 非阻塞线程，通知显示UI
        /// </summary>
        /// <param name="message"></param>
        /// <param name="type"></param>
        private void DisplayMessageToUI(PulseCommonHelper.Display display, PulseCommonHelper.Display.DisplayType type)
        {
            if (notifyMainForm != null)
            {
                notifyMainForm(display, type);
            }
        }

        private void DisplayMessageToUI(string message, LogMsgType type)
        {
            PulseCommonHelper.Display display = new PulseCommonHelper.Display();
            display.Message = message;
            display.LogLevel = type;
            if (notifyMainForm != null)
            {
                notifyMainForm(display, PulseCommonHelper.Display.DisplayType.eDISPLAY_TYPE_MSG);
            }
        }

        private void DisplayMessageToUI(string itemName, string itemInfo, bool result)
        {
            PulseCommonHelper.Display display = new PulseCommonHelper.Display();
            display.displayRltBox.itemName = itemName;
            display.displayRltBox.itemInfo = itemInfo;
            display.displayRltBox.result = result;
            if (notifyMainForm != null)
            {
                notifyMainForm(display, PulseCommonHelper.Display.DisplayType.eDISPLAY_TYPE_ITEM);
            }
        }

        private string CreateSaveRawDataFile()
        {
            if (mRawDataPath == null)
                return null;

            string strdate = DateTime.Now.ToString("yyyyMMdd");
            string str_hms = DateTime.Now.TimeOfDay.ToString("hhmmssfff");
            string logFileName = mRawDataPath + "\\" + String.Format("{0}-{1}-{2}-{3}-{4}-{5}-{6}-{7}-{8}-{9}.dat",
                "GMMX", mPhoneNumber, "3", mSerialNumber.Substring(4),
                strdate, str_hms,
                mThreeBodyPartBaseLineMap[BodyPart.eBODY_PART_CUN],
                mThreeBodyPartBaseLineMap[BodyPart.eBODY_PART_GUAN],
                mThreeBodyPartBaseLineMap[BodyPart.eBODY_PART_CHI],
                mUserRightHandSel ? "R" : "L");

            Log.Debug(TAG + "CreateSaveRawDataFile() Save file path: " + logFileName);
            return logFileName;
        }
    }
}
